package com.zmn.oms.business.impl.master;

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.zmn.base.oms.common.model.es.orderpart.EsOrderPart;
import com.zmn.common.utils.collection.CollectionUtil;
import com.zmn.common.utils.date.DateUtil;
import com.zmn.common.utils.number.NumberUtil;
import com.zmn.common.utils.reflect.BeanMapper;
import com.zmn.oms.business.impl.base.AbstractListEsQueryServiceImpl;
import com.zmn.oms.business.interfaces.es.OrderPartEsBService;
import com.zmn.oms.business.interfaces.master.MasterPartBService;
import com.zmn.oms.common.constant.OrderConsts;
import com.zmn.oms.common.constant.OrderPartConsts;
import com.zmn.oms.common.constant.OrderStatusConsts;
import com.zmn.oms.common.dro.normal.master.*;
import com.zmn.oms.common.exception.OmsBaseException;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedSum;
import org.elasticsearch.search.aggregations.metrics.ParsedValueCount;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author sunlife
 * @date:2021/7/29 3:45 下午
 * description:
 */
@Slf4j
@Service
public class MasterPartBServiceImpl extends AbstractListEsQueryServiceImpl implements MasterPartBService {

    @Autowired
    protected OrderPartEsBService orderPartEsBService;

    private static final String typeGroupFiled = "partType";
    private static final String orderIdGroupFiled = "orderId";

    @Override
    public MasterPartInfoDRO getMasterPartInfo(Integer masterId) throws OmsBaseException {
        String currMonthStr = DateUtil.toString(DateUtil.getNow(), DateUtil.FORMAT_MONTH);
        return this.getMasterPartInfo(masterId, currMonthStr, null);
    }

    @Override
    public MasterOrderPartInfoDRO listMonthPartInfo(Integer masterId, String date, Integer type) throws OmsBaseException {
        MasterOrderPartInfoDRO masterOrderPartInfoDRO = new MasterOrderPartInfoDRO();
        masterOrderPartInfoDRO.setDate(date);

        MasterPartInfoDRO masterPartInfo = this.getMasterPartInfo(masterId, date, type);
        BeanMapper.copy(masterPartInfo, masterOrderPartInfoDRO);

        List<OrderPartInfoDRO> orderPartInfoDROS = this.getPartInfoList(masterId, date, type);
        masterOrderPartInfoDRO.setPartList(orderPartInfoDROS);

        return masterOrderPartInfoDRO;
    }

    /**
     * 获取指定时间区域的统计信息
     * @param masterId 师傅ID
     * @param date 时间
     * @param type 配件类型
     * @return
     * @throws OmsBaseException
     */
    private MasterPartInfoDRO getMasterPartInfo(Integer masterId, String date, Integer type) throws OmsBaseException {
        NativeSearchQuery searchQuery = this.getBaseSearchQuery(masterId, date, type);

        // 分组
        TermsAggregationBuilder typeAggregationBuilder = AggregationBuilders.terms(typeGroupFiled).field(typeGroupFiled);

        typeAggregationBuilder.subAggregation(AggregationBuilders.sum("amount").field("amount"));
        searchQuery.addAggregation(typeAggregationBuilder);

        // 订单ID分组
        TermsAggregationBuilder countAggregationBuilder = AggregationBuilders.terms(orderIdGroupFiled).field(orderIdGroupFiled).size(10000);

        // partType
        TermsAggregationBuilder subPartTypeAggregationBuilder = AggregationBuilders.terms("partType").field("partType");
        countAggregationBuilder.subAggregation(subPartTypeAggregationBuilder);

        // 计数
        subPartTypeAggregationBuilder.subAggregation(AggregationBuilders.count("orderId").field("orderId"));


        searchQuery.addAggregation(countAggregationBuilder);
        Aggregations query = orderPartEsBService.getAggregationsByQuery(searchQuery);

        List<? extends Terms.Bucket> typeBuckets;
        List<? extends Terms.Bucket> orderIdBuckets;

        // 处理金额
        ParsedLongTerms typeTerms = query.get(typeGroupFiled);
        typeBuckets = typeTerms.getBuckets();

        // 订单数量
        ParsedLongTerms orderIdTerms = query.get(orderIdGroupFiled);
        orderIdBuckets = orderIdTerms.getBuckets();

        MasterPartInfoDRO masterPartInfo = new MasterPartInfoDRO();
        if (CollectionUtil.isNullOrEmpty(typeBuckets) && CollectionUtil.isNullOrEmpty(orderIdBuckets)) {
            return masterPartInfo;
        }

        typeBuckets.forEach(bucket -> {
            Map<String, Aggregation> stringAggregationMap = bucket.getAggregations().asMap();

            Integer partType = bucket.getKeyAsNumber().intValue();

            if (Objects.equals(partType, OrderConsts.PART_TYPE_SOURCING_IN)) {
                masterPartInfo.setInSourceCost((int)((ParsedSum) stringAggregationMap.get("amount")).getValue());
            } else if (Objects.equals(partType, OrderConsts.PART_TYPE_SOURCING_OUT)) {
                masterPartInfo.setOutSourceCost((int)((ParsedSum) stringAggregationMap.get("amount")).getValue());
            } else if (Objects.equals(partType, OrderConsts.PART_TYPE_DELIVER)) {
                masterPartInfo.setDeliverCost((int)((ParsedSum) stringAggregationMap.get("amount")).getValue());
            }
        });

        orderIdBuckets.forEach(bucket -> {
            Map<String, Aggregation> aggregationMap = bucket.getAggregations().asMap();

            Long orderId = bucket.getKeyAsNumber().longValue();

            ParsedLongTerms partTypeTerms = (ParsedLongTerms) aggregationMap.get("partType");
            partTypeTerms.getBuckets().forEach(partTypeBucket -> {
                ParsedValueCount count = (ParsedValueCount)partTypeBucket.getAggregations().asMap().get("orderId");

                Integer partType = partTypeBucket.getKeyAsNumber().intValue();
                Integer orderCount = (int)count.getValue();

                if (NumberUtil.isNotNullOrZero(orderCount)) {
                    if (Objects.equals(partType, OrderConsts.PART_TYPE_SOURCING_IN)) {
                        masterPartInfo.setInSourceOrderCount(Optional.ofNullable(masterPartInfo.getInSourceOrderCount()).orElse(0) + orderCount);
                    } else if (Objects.equals(partType, OrderConsts.PART_TYPE_SOURCING_OUT)) {
                        masterPartInfo.setOutSourceOrderCount(Optional.ofNullable(masterPartInfo.getOutSourceOrderCount()).orElse(0) + orderCount);
                    } else if (Objects.equals(partType, OrderConsts.PART_TYPE_DELIVER)) {
                        masterPartInfo.setDeliverOrderCount(Optional.ofNullable(masterPartInfo.getDeliverOrderCount()).orElse(0) + orderCount);
                    }
                }
            });
        });

        Integer totalPartAmount = 0;
        totalPartAmount += Optional.ofNullable(masterPartInfo.getInSourceCost()).orElse(0);
        totalPartAmount += Optional.ofNullable(masterPartInfo.getOutSourceCost()).orElse(0);
        totalPartAmount += Optional.ofNullable(masterPartInfo.getDeliverCost()).orElse(0);
        masterPartInfo.setAccumulateCost(totalPartAmount);

        return masterPartInfo;
    }

    private List<OrderPartInfoDRO> getPartInfoList(Integer masterId, String date, Integer type) throws OmsBaseException {
        List<OrderPartInfoDRO> orderPartInfoDROS = Lists.newArrayList();

        NativeSearchQuery searchQuery = this.getBaseSearchQuery(masterId, date, type);
        searchQuery.setPageable(PageRequest.of(0, 10000, Sort.by(Sort.Order.desc("accountTime"))));
        FetchSourceFilter fetchSourceFilter = new FetchSourceFilter(new String[]{"orderPartId", "orderId", "productInfo", "amount", "partType", "accountTime", "reimburseTime"}, new String[]{});
        searchQuery.addSourceFilter(fetchSourceFilter);
        log.debug("师傅配件列表查询[{}]", searchQuery.getQuery().toString());
        List<EsOrderPart> page = orderPartEsBService.listPageByQuery(searchQuery);
        log.debug("师傅配件列表查询 结果[{}]", JSON.toJSONString(page));
        if (CollectionUtil.isNotNullOrEmpty(page)) {
            Map<Long, List<EsOrderPart>> orderPartMap = page.stream().collect(Collectors.groupingBy(EsOrderPart::getOrderId));
            orderPartMap.forEach((orderId, esOrderParts) -> {
                EsOrderPart esOrderPart = esOrderParts.get(0);

                OrderPartInfoDRO orderPartInfoDRO = new OrderPartInfoDRO();

                orderPartInfoDRO.setOrderId(orderId);
                orderPartInfoDRO.setItemName(esOrderPart.getProductInfo());

                Integer totalPartAmount = esOrderParts.stream().mapToInt(EsOrderPart::getAmount).sum();
                orderPartInfoDRO.setPartAmount(totalPartAmount);

                Date accountTime = null;

                Optional<EsOrderPart> optional = esOrderParts.stream().filter(part -> {
                    return Objects.equals(part.getPartType(), OrderConsts.PART_TYPE_SOURCING_OUT) && Objects.nonNull(part.getReimburseTime());
                }).findFirst();
                if (optional.isPresent()) {
                    accountTime = optional.get().getReimburseTime();
                }
                if (accountTime == null) {
                    Optional<EsOrderPart> accountOptional = esOrderParts.stream().filter(part -> {
                        return Objects.nonNull(part.getAccountTime());
                    }).findFirst();
                    if (accountOptional.isPresent()) {
                        accountTime = accountOptional.get().getAccountTime();
                    }
                }
                orderPartInfoDRO.setAccountTime(accountTime);
                orderPartInfoDROS.add(orderPartInfoDRO);
            });

            orderPartInfoDROS.sort(Comparator.comparing(OrderPartInfoDRO::getAccountTime).reversed());
        }

        return orderPartInfoDROS;
    }

    private NativeSearchQuery getBaseSearchQuery(Integer masterId, String date, Integer type) throws OmsBaseException {
        String startTime = null;
        String endTime = null;
        if (DateUtil.isValid(date, DateUtil.FORMAT_MONTH)) {
            Date formatDate = DateUtil.parse(date, DateUtil.FORMAT_MONTH);
            Calendar calendar = Calendar.getInstance();
            calendar.setTime(formatDate);
            int year = calendar.get(Calendar.YEAR);
            int month = calendar.get(Calendar.MONTH) + 1;
            startTime = DateUtil.toString(DateUtil.getMonthStart(year, month), DateUtil.FORMAT_DEFAULT);
            endTime = DateUtil.toString(DateUtil.getMonthEnd(year, month), DateUtil.FORMAT_DEFAULT);
        } else if (DateUtil.isValid(date, DateUtil.FORMAT_DATE)) {
            Date formatDate = DateUtil.parse(date, DateUtil.FORMAT_DATE);
            startTime = DateUtil.toString(DateUtil.getDateStart(formatDate), DateUtil.FORMAT_DEFAULT);
            endTime = DateUtil.toString(DateUtil.getDateEnd(formatDate), DateUtil.FORMAT_DEFAULT);
        } else {
            throw new OmsBaseException("不合格的日期格式");
        }

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .filter(QueryBuilders.termQuery("masterId", masterId))
                .mustNot(QueryBuilders.termQuery("payType",  OrderPartConsts.DEPOSIT_PAY))
                .filter(QueryBuilders.rangeQuery("accountTime").gte(startTime).lte(endTime));

        // 配件类型
        if (NumberUtil.isNotNullOrZero(type)) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("partType", type));
        }

        // 内采
        BoolQueryBuilder inQueryBuilder = QueryBuilders.boolQuery();
        inQueryBuilder.must(QueryBuilders.termQuery("partType", OrderConsts.PART_TYPE_SOURCING_IN));
        boolQueryBuilder.should(inQueryBuilder);

        // 其他配件
        BoolQueryBuilder otherQueryBuilder = QueryBuilders.boolQuery();
        otherQueryBuilder.mustNot(QueryBuilders.termQuery("partType", OrderConsts.PART_TYPE_SOURCING_IN))
                .must(QueryBuilders.termQuery("reimburseStatus", OrderConsts.PART_REIMBURSE_STATUS_SUCCESS));
        boolQueryBuilder.should(otherQueryBuilder);
        boolQueryBuilder.minimumShouldMatch(1);


        NativeSearchQuery searchQuery = new NativeSearchQuery(boolQueryBuilder);

        return searchQuery;
    }

    @Override
    public OrderPartDetailDRO getOrderPartDetail(Long orderId) throws OmsBaseException {
        OrderPartDetailDRO orderPartDetailDRO = new OrderPartDetailDRO();
        orderPartDetailDRO.setOrderId(orderId);

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .filter(QueryBuilders.termQuery("orderId", orderId))
                .mustNot(QueryBuilders.termQuery("payType",  OrderPartConsts.DEPOSIT_PAY));

        // 内采
        BoolQueryBuilder inQueryBuilder = QueryBuilders.boolQuery();
        inQueryBuilder.must(QueryBuilders.termQuery("partType", OrderConsts.PART_TYPE_SOURCING_IN));
        boolQueryBuilder.should(inQueryBuilder);

        // 其他配件
        BoolQueryBuilder otherQueryBuilder = QueryBuilders.boolQuery();
        otherQueryBuilder.mustNot(QueryBuilders.termQuery("partType", OrderConsts.PART_TYPE_SOURCING_IN))
                .must(QueryBuilders.termQuery("reimburseStatus", OrderConsts.PART_REIMBURSE_STATUS_SUCCESS));
        boolQueryBuilder.should(otherQueryBuilder);
        boolQueryBuilder.minimumShouldMatch(1);

        NativeSearchQuery searchQuery = new NativeSearchQuery(boolQueryBuilder);

        searchQuery.setPageable(PageRequest.of(0, 10000, Sort.by(Sort.Order.desc("accountTime"))));
        FetchSourceFilter fetchSourceFilter = new FetchSourceFilter(new String[]{"orderPartId", "orderId", "status", "partName", "number", "price", "amount", "partType", "accountTime", "reimburseTime"}, new String[]{});
        searchQuery.addSourceFilter(fetchSourceFilter);
        log.debug("师傅配件列表查询[{}]", searchQuery.getQuery().toString());
        List<EsOrderPart> esOrderParts = orderPartEsBService.listPageByQuery(searchQuery);
        log.debug("师傅配件列表查询 结果[{}]", JSON.toJSONString(esOrderParts));
        if (CollectionUtil.isNotNullOrEmpty(esOrderParts)) {
            EsOrderPart orderPart = esOrderParts.get(0);
            orderPartDetailDRO.setStatus(orderPart.getStatus());
            orderPartDetailDRO.setStatusName(OrderStatusConsts.getWorkStatusName(orderPart.getStatus()));

            Date accountTime = null;
            Optional<EsOrderPart> optional = esOrderParts.stream().filter(part -> {
                return Objects.equals(part.getPartType(), OrderConsts.PART_TYPE_SOURCING_OUT) && Objects.nonNull(part.getReimburseTime());
            }).findFirst();
            if (optional.isPresent()) {
                accountTime = optional.get().getReimburseTime();
            }
            if (accountTime == null) {
                Optional<EsOrderPart> accountOptional = esOrderParts.stream().filter(part -> {
                    return Objects.nonNull(part.getAccountTime());
                }).findFirst();
                if (accountOptional.isPresent()) {
                    accountTime = accountOptional.get().getAccountTime();
                }
            }
            orderPartDetailDRO.setAccountTime(accountTime);

            Map<Integer, List<EsOrderPart>> orderPartMap = esOrderParts.stream().collect(Collectors.groupingBy(EsOrderPart::getPartType));
            orderPartMap.forEach((partType, orderParts) -> {
                List<PartDetailDRO> partDetailList = BeanMapper.mapList(orderParts, PartDetailDRO.class);
                Integer amount = partDetailList.stream().mapToInt(PartDetailDRO::getAmount).sum();
                if (Objects.equals(partType, OrderConsts.PART_TYPE_SOURCING_IN)) {
                    orderPartDetailDRO.setInSourcePartList(partDetailList);
                    orderPartDetailDRO.setInSourceAmount(amount);
                } else if (Objects.equals(partType, OrderConsts.PART_TYPE_SOURCING_OUT)) {
                    orderPartDetailDRO.setOutSourcePartList(partDetailList);
                    orderPartDetailDRO.setOutSourceAmount(amount);
                } else if (Objects.equals(partType, OrderConsts.PART_TYPE_DELIVER)) {
                    orderPartDetailDRO.setDeliverPartList(partDetailList);
                    orderPartDetailDRO.setDeliverAmount(amount);
                }
            });

            Integer totalAmount = esOrderParts.stream().mapToInt(EsOrderPart::getAmount).sum();
            orderPartDetailDRO.setTotalAmount(totalAmount);
        }

        return orderPartDetailDRO;
    }
}
